/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package stud.cp.user.dbaccess;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.LinkedList;
import java.util.List;
import org.apache.log4j.Logger;
import stud.cp.core.dbaccess.DAO;
import stud.cp.core.dbaccess.DAOException;
import stud.cp.user.valueobjects.Property;

/**
 * Класс, отвечающий за логику
 * доступа к таблице property
 * @author Morfant
 */
public class PropertiesDAO implements DAO<Property> {

    /**
     * Логгер.
     */
    private static Logger logger = Logger.getLogger("CPTNP");
    /**
     * Список названий колонок таблицы properties..
     */
    private static String[] propetyColumnName = {"property_id",
        "property_type_id",
        "property_group_id",
        "name",
        "property_type_def_id"};
    /**
     * Имя колонок таблицы property_types
     */
    private static String[] propertyTypeColumnName = {"property_type_id", "type_name"};
    /**
     * Имя колонок таблицы propertyGroups
     */
    private static String[] propertyGroupsColumnName = {"property_group_id", "group_name"};
    /**
     * Коннект к базе данных.
     */
    private Connection connection = null;

    /**
     * Конструктор, который устанавливает
     * коннект к базе данных.
     * @param connection Связь с базой данных.
     */
    public PropertiesDAO(Connection connection) {
        this.connection = connection;
    }

    /**
     * Запрос, позволяющий получить запись по id.
     * @param id ID
     * @return Запрос.
     */
    private String getQueryForGetById(int id) {
        StringBuilder query = new StringBuilder();

        query.append("SELECT p.property_id,p.property_type_id, p.property_group_id,p.name,p.property_type_def_id");
        query.append(",pt.name as \"type_name\",pg.name as \"group_name\" \n");
        query.append("FROM properties p \n");
        query.append("inner join property_types pt on p.property_type_id=pt.property_type_id \n");
        query.append("inner join property_groups pg on p.property_group_id=pg.property_group_id \n");
        query.append("WHERE property_id=");
        query.append(id);

        return query.toString();
    }

    /**
     * Метод, позволяющий получить запись
     * по её ID
     * @param id ID
     * @return Данные записи.
     * @throws DAOException
     * <p>
     *  Ошикба доступа к данным.
     * </p>
     */
    @Override
    public Property getById(int id) throws DAOException {
        Property property = null;
        ResultSet set = null;
        PreparedStatement prestat = null;
        try {
            prestat = connection.prepareStatement(getQueryForGetById(id));
            set = prestat.executeQuery();
            if (set.next()) {
                property =
                        new Property(set.getInt(propetyColumnName[0]),
                        set.getInt(propetyColumnName[1]),
                        set.getInt(propetyColumnName[2]),
                        set.getString(propetyColumnName[3]),
                        set.getInt(propetyColumnName[4]),
                        set.getString(propertyTypeColumnName[1]),
                        set.getString(propertyGroupsColumnName[1]));
            }
        } catch (SQLException ex) {
            logger.debug("DAO_propety", ex);
            throw new DAOException("Ошибка при создание данных..", ex);
        } finally {
            if (prestat != null) {
                try {
                    prestat.close();
                } catch (SQLException ex) {
                    logger.debug("DAO_propety", ex);
                    throw new DAOException("Ошибка закрытия ResultSet`а.", ex);
                }
            }
        }
        return property;
    }

    /**
     *  Запрос на добавление
     * новой записи в таблицу.
     * @param property_id ID свойства
     * @param property_type_id ID типа свойства
     * @param property_group_id ID группы свойств
     * @param name Имя
     * @param property_type_defs_id ID определения типа свойств
     * @return Запрос
     */
    private String getQueryForInsert(Integer property_id,
            Integer property_type_id, Integer property_group_id,
            String name, Integer property_type_def_id) {
        StringBuilder query = new StringBuilder();
        query.append("INSERT INTO properties(property_id,property_type_id, property_group_id,name,property_type_def_id) \n");
        query.append("VALUES(");
        query.append(property_id);
        query.append(",");
        query.append(property_type_id);
        query.append(",");
        query.append(property_group_id);
        query.append(",'");
        query.append(name);
        query.append("',");
        query.append(property_type_def_id);
        query.append(")");
        return query.toString();
    }

    /**
     * Зарпос на получения следующего ID.
     * @return Зарпос.
     */
    private String getQueryForSelectNextID() {
        StringBuilder query = new StringBuilder();
        query.append("SELECT properties_seq.NEXTVAL from dual \n");
        return query.toString();
    }

    /**
     * Зарпос на получения максимального id из таблицы.
     * @return Зарпос.
     */
    private String getQueryForSelectMaxId() {
        StringBuilder query = new StringBuilder();
        query.append("SELECT MAX(property_id) \n");
        query.append("FROM properties");
        return query.toString();
    }

    /**
     * Запрос на обновление записи в таблице.
     * @param property_id ID свойства
     * @param property_type_id ID типа свойства
     * @param property_group_id ID группы свойств
     * @param name Имя
     * @param property_type_defs_id ID определения типа свойств
     * @return Запрос.
     */
    private String getQueryForUpdate(Integer property_id, Integer property_group_id,
            String name) {
        StringBuilder query = new StringBuilder();
        query.append("UPDATE properties \n");
        query.append("SET property_group_id=");
        query.append(property_group_id);
        query.append(", \n");
        query.append("name='");
        query.append(name);
        query.append("' \n");
        query.append("WHERE property_id=");
        query.append(property_id);
        return query.toString();
    }

    /**
     * Создание новой записи..
     * @param entry Запись.
     * @throws DAOException
     * <p>
     * Ошибка сохранения данных.
     * </p>
     */
    @Override
    public int create(Property entry) throws DAOException {
        ResultSet set = null;
        PreparedStatement prestat = null;
        try {
            prestat = connection.prepareStatement(getQueryForSelectNextID());
            set = prestat.executeQuery();
            Integer index = null;
            if (set.next()) {
                index = set.getInt(1);
            } else {
                index = 0;
            }
            prestat.close();
            prestat = connection.prepareStatement(
                    getQueryForInsert(index,
                    entry.getProperty_type_id(),
                    entry.getProperty_group_id(),
                    entry.getName(),
                    entry.getProperty_type_defs_id()));
            prestat.executeUpdate();
            return index;
        } catch (SQLException ex) {
            logger.debug("DAO_propeties", ex);
            throw new DAOException("Ошибка при создание данных..", ex);
        } finally {
            if (prestat != null) {
                try {
                    prestat.close();
                } catch (SQLException ex) {
                    logger.debug("DAO_propety", ex);
                    throw new DAOException("Ошибка закрытия ResultSet`а.", ex);
                }
            }
        }
    }

    /**
     * Метод, позволяющий обновить данные.
     * @param entry Запись.
     * @throws DAOException
     * <p>
     * Ошибка обновления данных.
     * </p>
     */
    @Override
    public void update(Property entry) throws DAOException {
        PreparedStatement prestat = null;
        try {
            prestat = connection.prepareStatement(
                    getQueryForUpdate(entry.getProperty_id(),
                    entry.getProperty_group_id(),
                    entry.getName()));
            prestat.executeQuery();
        } catch (SQLException ex) {
            logger.debug("DAO_propeties", ex);
            throw new DAOException("Ошибка обновления данных.", ex);
        } finally {
            if (prestat != null) {
                try {
                    prestat.close();
                } catch (SQLException ex) {
                    logger.debug("DAO_propety", ex);
                    throw new DAOException("Ошибка закрытия ResultSet`а.", ex);
                }
            }
        }
    }

    /**
     * Запрос, позволяющий при выполнении
     * получить полностью данные из таблицы
     * @return Строка запроса.
     */
    private String getQueryForAllProperty() {
        StringBuilder query = new StringBuilder();
        query.append("select p.property_id,p.property_type_id, p.property_group_id,p.name,p.property_type_def_id");
        query.append(",pt.name as \"type_name\",pg.name as \"group_name\" \n");
        query.append("from properties  p \n");
        query.append("inner join property_types pt on p.property_type_id=pt.property_type_id \n");
        query.append("inner join property_groups pg on p.property_group_id=pg.property_group_id \n");
        return query.toString();
    }

    /**
     * Метод, который возвращает
     * список значений из таблицы
     * @return Список данных.
     */
    @Override
    public List<Property> getAll() throws DAOException {
        List<Property> data_list = new LinkedList<Property>();
        ResultSet set = null;
        PreparedStatement prestat = null;
        try {
            prestat = connection.prepareStatement(getQueryForAllProperty());
            set = prestat.executeQuery();
            while (set.next()) {
                data_list.add(new Property(set.getInt(propetyColumnName[0]),
                        set.getInt(propetyColumnName[1]),
                        set.getInt(propetyColumnName[2]),
                        set.getString(propetyColumnName[3]),
                        set.getInt(propetyColumnName[4]),
                        set.getString(propertyTypeColumnName[1]),
                        set.getString(propertyGroupsColumnName[1])));
            }
        } catch (SQLException ex) {
            logger.debug("DAO_propeties", ex);
            throw new DAOException("Ошибка получения списка данных.", ex);
        } finally {
            if (prestat != null) {
                try {
                    prestat.close();
                } catch (SQLException ex) {
                    logger.debug("DAO_propety", ex);
                    throw new DAOException("Ошибка закрытия ResultSet`а.", ex);
                }
            }
        }
        return data_list;
    }

    /**
     * Запрос, позволяющий при выполнении
     * получить список свойств принадлежащие
     * определенному типу оружия.
     * @param weapon_type_id ID типа оружия.
     * @return Строка запроса.
     */
    private String getQueryForAllPropertyForWeapon(int weapon_type_id) {
        StringBuilder query = new StringBuilder();
        query.append("select p.property_id,p.property_type_id, p.property_group_id,p.name,p.property_type_def_id");
        query.append(",pt.name as \"type_name\",pg.name as \"group_name\" \n");
        query.append("from properties  p \n");
        query.append("inner join property_types pt on p.property_type_id=pt.property_type_id \n");
        query.append("inner join property_groups pg on p.property_group_id=pg.property_group_id \n");
        query.append("inner join weapon_types_properties wtp on p.property_id=wtp.property_id \n");
        query.append("where wtp.weapon_type_id=");
        query.append(weapon_type_id);
        query.append("\n order by pg.name");
        return query.toString();
    }

    /**
     * Запрос, который позволяет получить
     * все свойства для типа оружия.
     * @param weapon_type_id ID типа оружия.
     * @return Список свойств.
     * @throws DAOException Ошибка доступа к данным.
     */
    public List<Property> getAllPropertyForWeaponType(int weapon_type_id) throws DAOException {
        List<Property> data_list = new LinkedList<Property>();
        ResultSet set = null;
        PreparedStatement prestat = null;
        try {
            prestat =
                    connection.prepareStatement(
                    getQueryForAllPropertyForWeapon(weapon_type_id));
            set = prestat.executeQuery();
            while (set.next()) {
                data_list.add(new Property(set.getInt(propetyColumnName[0]),
                        set.getInt(propetyColumnName[1]),
                        set.getInt(propetyColumnName[2]),
                        set.getString(propetyColumnName[3]),
                        set.getInt(propetyColumnName[4]),
                        set.getString(propertyTypeColumnName[1]),
                        set.getString(propertyGroupsColumnName[1])));
            }
        } catch (SQLException ex) {
            logger.debug("DAO_propeties", ex);
            throw new DAOException("Ошибка получения списка данных для определенного типа оружия.", ex);
        } finally {
            if (prestat != null) {
                try {
                    prestat.close();
                } catch (SQLException ex) {
                    logger.debug("DAO_propety", ex);
                    throw new DAOException("Ошибка закрытия ResultSet`а.", ex);
                }
            }
        }
        return data_list;
    }

    /**
     * Запрос, позволяющий при выполнении
     * получить список свойств не принадлежащих
     * определенному типу оружия.
     * @param weapon_type_id ID типа оружия.
     * @return Строка запроса.
     */
    private String getQueryForAllPropertiesThatNotBelongToWeaponType(int weapon_type_id) {
        StringBuilder query = new StringBuilder();
        query.append("select p.property_id,p.property_type_id, p.property_group_id,p.name,p.property_type_def_id");
        query.append(",pt.name as \"type_name\",pg.name as \"group_name\" \n");
        query.append("from properties  p \n");
        query.append("inner join property_types pt on p.property_type_id=pt.property_type_id \n");
        query.append("inner join property_groups pg on p.property_group_id=pg.property_group_id \n");
        query.append("where p.property_id not in \n");
        query.append("(select wtp.property_id \n");
        query.append("from weapon_types_properties wtp \n");
        query.append("where wtp.weapon_type_id=");
        query.append(weapon_type_id);
        query.append(")");
        return query.toString();
    }

    /**
     * Запрос, который выдает все свойств,
     * который не включены в тип оружия.
     * @param weapon_type_id ID типа оружия.
     * @return Список свойств.
     * @throws DAOException Ошибка доступа к данным.
     */
    public List<Property> getAllPropertiesThatNotBelongToWeaponType(int weapon_type_id) throws DAOException {
        List<Property> data_list = new LinkedList<Property>();
        ResultSet set = null;
        PreparedStatement prestat = null;
        try {
            prestat =
                    connection.prepareStatement(getQueryForAllPropertiesThatNotBelongToWeaponType(weapon_type_id));
            set = prestat.executeQuery();
            while (set.next()) {
                data_list.add(new Property(set.getInt(propetyColumnName[0]),
                        set.getInt(propetyColumnName[1]),
                        set.getInt(propetyColumnName[2]),
                        set.getString(propetyColumnName[3]),
                        set.getInt(propetyColumnName[4]),
                        set.getString(propertyTypeColumnName[1]),
                        set.getString(propertyGroupsColumnName[1])));
            }
        } catch (SQLException ex) {
            logger.debug("DAO_propeties", ex);
            throw new DAOException("Ошибка получения списка данных.", ex);
        } finally {
            if (prestat != null) {
                try {
                    prestat.close();
                } catch (SQLException ex) {
                    logger.debug("DAO_propety", ex);
                    throw new DAOException("Ошибка закрытия ResultSet`а.", ex);
                }
            }
        }
        return data_list;
    }

    /**
     * Зарпос на удаление из таблицы
     * записи по id.
     * @param id ID
     * @return Зарпос.
     */
    private String getQueryForDelete(int id) {
        StringBuilder query = new StringBuilder();
        query.append("DELETE FROM properties \n");
        query.append("WHERE property_id=");
        query.append(id);
        return query.toString();
    }

    /**
     * Удаление из табилцы
     * weapon_types_properties.
     * @param property_id ID свойства.
     * @return  Зарпос.
     */
    private String getQueryForDeleteFromWeaponTypesProperties(int property_id) {
        StringBuilder query = new StringBuilder();
        query.append("DELETE FROM weapon_types_properties \n");
        query.append("WHERE property_id=");
        query.append(property_id);
        return query.toString();
    }

    /**
     * Удаление из табилцы
     * property_values.
     * @param property_id ID свойства.
     * @return  Зарпос.
     */
    private String getQueryForDeleteFromPropertyValues(int property_id) {
        StringBuilder query = new StringBuilder();
        query.append("DELETE FROM property_values \n");
        query.append("WHERE property_id=");
        query.append(property_id);
        return query.toString();
    }

    /**
     * Удаление из табилцы
     * weapon_references.
     * @param property_id ID свойства.
     * @return  Зарпос.
     */
    private String getQueryForDeleteFromWeaponReferences(int property_id) {
        StringBuilder query = new StringBuilder();
        query.append("DELETE FROM weapon_references \n");
        query.append("WHERE property_id=");
        query.append(property_id);
        return query.toString();
    }

    /**
     * Удаление истории свойства.
     * @param id ID свойства.
     * @return Запрос.
     */
    private String getQueryForDeletePropertyHistory(int id) {
        StringBuilder query = new StringBuilder();
        query.append("DELETE FROM history \n");
        query.append("WHERE property_id=");
        query.append(id);
        return query.toString();
    }

    /**
     * Метод, позволяющий удалять данные по записи.
     * @param id Запись.
     * @throws DAOException
     */
    @Override
    public void delete(int id) throws DAOException {
        PreparedStatement prestat = null;
        try {
            prestat = connection.prepareStatement(getQueryForDeleteFromWeaponTypesProperties(id));
            prestat.executeUpdate();
            prestat.close();
            prestat = connection.prepareStatement(getQueryForDeletePropertyHistory(id));
            prestat.executeUpdate();
            prestat.close();
            prestat = connection.prepareStatement(getQueryForDeleteFromPropertyValues(id));
            prestat.executeUpdate();
            prestat.close();
            prestat = connection.prepareStatement(getQueryForDeleteFromWeaponReferences(id));
            prestat.executeUpdate();
            prestat.close();
            prestat = connection.prepareStatement(getQueryForDelete(id));
            prestat.executeUpdate();
        } catch (SQLException ex) {
            logger.debug("DAO_propeties", ex);
            throw new DAOException("Ошибка удаления данных.", ex);
        } finally {
            if (prestat != null) {
                try {
                    prestat.close();
                } catch (SQLException ex) {
                    logger.debug("DAO_propety", ex);
                    throw new DAOException("Ошибка закрытия ResultSet`а.", ex);
                }
            }
        }
    }
}
